﻿Imports System
Imports System.Threading

Namespace System.Workflow.Runtime.Hosting
    Friend Class SmartTimer
        Implements IDisposable
        ' Methods
        Public Sub New(ByVal callback As TimerCallback, ByVal state As Object, ByVal due As TimeSpan, ByVal period As TimeSpan)
            Me.period = period
            Me.callback = callback
            Me.next = (DateTime.UtcNow + due)
            Me.timer = New Timer(New TimerCallback(AddressOf Me.HandleCallback), state, due, Me.infinite)
        End Sub

        Public Sub Dispose()
            SyncLock Me.locker
                If (Not Me.timer Is Nothing) Then
                    Me.timer.Dispose
                    Me.timer = Nothing
                End If
            End SyncLock
        End Sub

        Private Sub HandleCallback(ByVal state As Object)
            Try 
                Me.callback.Invoke(state)
            Finally
                SyncLock Me.locker
                    If (Not Me.timer Is Nothing) Then
                        If Not Me.nextChanged Then
                            Me.next = (DateTime.UtcNow + Me.period)
                        Else
                            Me.nextChanged = False
                        End If
                        Dim dueTime As TimeSpan = DirectCast((Me.next - DateTime.UtcNow), TimeSpan)
                        If (dueTime < TimeSpan.Zero) Then
                            dueTime = TimeSpan.Zero
                        End If
                        Me.timer.Change(dueTime, Me.infinite)
                    End If
                End SyncLock
            End Try
        End Sub

        Public Sub Update(ByVal newNext As DateTime)
            If ((newNext < Me.next) AndAlso ((Me.next - DateTime.UtcNow) > Me.minUpdate)) Then
                SyncLock Me.locker
                    If (((newNext < Me.next) AndAlso ((Me.next - DateTime.UtcNow) > Me.minUpdate)) AndAlso (Not Me.timer Is Nothing)) Then
                        Me.next = newNext
                        Me.nextChanged = True
                        Dim dueTime As TimeSpan = DirectCast((Me.next - DateTime.UtcNow), TimeSpan)
                        If (dueTime < TimeSpan.Zero) Then
                            dueTime = TimeSpan.Zero
                        End If
                        Me.timer.Change(dueTime, Me.infinite)
                    End If
                End SyncLock
            End If
        End Sub


        ' Fields
        Private callback As TimerCallback
        Private infinite As TimeSpan = New TimeSpan(-1)
        Private locker As Object = New Object
        Private minUpdate As TimeSpan = New TimeSpan(0, 0, 5)
        Private [next] As DateTime
        Private nextChanged As Boolean
        Private period As TimeSpan
        Private timer As Timer
    End Class
End Namespace

